/*
 * ELF executable loading
 * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
 * Copyright (c) 2003, David H. Hovemeyer <daveho@cs.umd.edu>
 * $Revision: 1.29 $
 * 
 * This is free software.  You are permitted to use,
 * redistribute, and modify it as specified in the file "COPYING".
 */

#include <geekos/errno.h>
#include <geekos/kassert.h>
#include <geekos/ktypes.h>
#include <geekos/screen.h>  /* for debug Print() statements */
#include <geekos/pfat.h>
#include <geekos/malloc.h>
#include <geekos/string.h>
#include <geekos/user.h>
#include <geekos/elf.h>


/**
 * From the data of an ELF executable, determine how its segments
 * need to be loaded into memory.
 * @param exeFileData buffer containing the executable file
 * @param exeFileLength length of the executable file in bytes
 * @param exeFormat structure describing the executable's segments
 *   and entry address; to be filled in
 * @return 0 if successful, < 0 on error
 */
int Parse_ELF_Executable(char *exeFileData, ulong_t exeFileLength,
    struct Exe_Format *exeFormat)
{
    elfHeader *hElf;    /*Puntero a la estructura elfHeader, se utiliza para almacenar ELF HEADER del programa*/
    char *offsetPHT;    /*Puntero auxiliar para calcular la posicion de la proxima entrada a la tabla Program Header Table*/
    programHeader *hProg;   /*Puntero a la estructura programHeader, se utiliza para almacenar una entrada de Program Header Table*/
    int segment = 0;  /*Indice del segmento que se esta leyendo*/
    static int progdebug = 0;   /*Se utiliza para debug*/
    
    hElf = (elfHeader*) exeFileData;    /*Recuperamos la cabecera elf*/
    
    if ((hElf->type != 2) || (hElf->ident[1] != 'E') || (hElf->ident[2] != 'L') || (hElf->ident[3] != 'F')){   /*Verificamos que el tipo de archivo sea un elf ejecutable*/
            Print("The object file type is not ELF Executable... \n");
            return ENOEXEC;  /*Error*/
    }
    
    if (hElf->machine != 3){   /*Verificamos que el archivo objeto se haya generado para una arquitectura intel 80386*/
            Print("The object file's architecture is not intel 80386 \n");
            return EUNSPECIFIED;  /*Error*/
    }
    
    exeFormat->entryAddr = hElf->entry;
    exeFormat->numSegments = hElf->phnum;
    
    if (hElf->phnum != EXE_MAX_SEGMENTS)    /*Controlamos que el programa no tenga mas segmentos de los que puede manejar el kernel*/
        return EUNSPECIFIED;  /*Error*/
        
    offsetPHT = exeFileData;
    
    if (progdebug)
    {
      Set_Current_Attr(ATTRIB(BLACK, AMBER|BRIGHT));
      Print("Los hediondos del caribe presentan a:.....GUICOS SISTEMA DESOPERATIVO \n");
      Set_Current_Attr(ATTRIB(BLACK, GRAY));
      Print("Ident 0 = 0x%x\n",hElf->ident[0]);
      Print("Ident 1 = %c\n",hElf->ident[1]);
      Print("Ident 2 = %c\n",hElf->ident[2]);
      Print("Ident 3 = %c\n",hElf->ident[3]);
      Print("Object file type is: %d\n",hElf->type);
      Print("Object file architecture is: %d\n",hElf->machine);
      Print("start address: %ld\n",exeFormat->entryAddr);
      Print("number of entries in the program header table: %d\n",exeFormat->numSegments);
    }
    while (segment < hElf->phnum){    /*Obtenemos los demas segmentos*/
        if (segment == 0){
            offsetPHT = offsetPHT + hElf->phoff;    /*Se ubica la primer entrada de la tabla Program Header Table respecto de la direccion base*/
        }else{
            offsetPHT = offsetPHT + hElf->phentsize;    /*Se ubica la siguiente entrada de la tabla Program Header Table respecto de la direccion anterior*/
        }
        hProg = (programHeader *) offsetPHT;    /*Recuperamos una entrada en la tabla Program Header Table*/
        exeFormat->segmentList[segment].offsetInFile = hProg-> offset;
        exeFormat->segmentList[segment].lengthInFile = hProg-> fileSize;
        exeFormat->segmentList[segment].startAddress = hProg-> vaddr; 
        exeFormat->segmentList[segment].sizeInMemory = hProg->memSize ;	 
        exeFormat->segmentList[segment].protFlags = hProg->flags ;
        if (progdebug)
        {
          Set_Current_Attr(ATTRIB(BLACK, (segment+2)));
          Print("segmento: %d\n",segment+1);
          Print("\toffset: 0x%x",(int) exeFormat->segmentList[segment].offsetInFile);
          Print("\tfileSize: 0x%x",(int) exeFormat->segmentList[segment].lengthInFile);
          Print("\tvaddr: 0x%x",(int) exeFormat->segmentList[segment].startAddress);
          Print("\tmemSize: 0x%x",(int) exeFormat->segmentList[segment].sizeInMemory);
          Print("\tflags: %d\n",exeFormat->segmentList[segment].protFlags);
          Set_Current_Attr(ATTRIB(BLACK, GRAY));
        }
        segment = segment + 1;
    } 
    return 0;   /*Exit 0*/  
}

